grammar FadeLang;

/*
 * Parser Rules
 */

chunk 
    : block+ EOF
    ;

block 
    : stat+
    ;

stat  
    : declarationStat
	| functiondef
	| functionCall ';'
	| if
	| assignment ';'
	| for
	| return
	| imports
    ;

imports
	: 'import' string ';'
	;

return
    : 'return' exp ';'
	;

for
	: 'for' '('declarationStat exp ';' assignment')' '{' block? '}'
	;

assignment
	: NAME op=( '=' | '+=' | '-=' | '*=' | '/=' | '%=' ) exp
	;

if
    : 'if' '(' exp ')' '{' block '}' elifList? ('else' '{' block '}')?
	;

elifList
    : elif+
	;

elif
    : 'elif' '(' exp ')' '{' block '}'
	;

functionCall
    : NAME '(' argList ')'
	;

argList
    : exp? (','  exp)*
	;

explist
    : exp (',' exp)*
    ;

exp
    : literals
	| NAME
	| functionCall
	| op='-' exp								
	| exp op=( '*' | '/' | '%' ) exp		
	| exp op=( '+' | '-' ) exp						
	| <assoc=right> exp op='+' exp
	| exp op=('<' | '>' | '<=' | '>=' | '!=' | '==') exp					
	| exp op='and' exp
	| exp op='or' exp	
	;

literals
    : null | false | true | number | string
	;
   
null
    : 'null'
	;

false
    : 'false'
	;

true
    : 'true'
	;

declarationStat
    : 'var' NAME '=' exp ';'
    ;
	
number
    : INT | HEX | FLOAT | HEX_FLOAT
    ;

string
    : NORMALSTRING | CHARSTRING
    ;

functiondef
    : 'def' NAME funcbody
    ;

funcbody
    : '(' namelist? ')' '{' block? '}'
    ;

namelist
    : NAME (',' NAME)*
    ;

/*
 * Lexer Rules
 */

NORMALSTRING
    : '"' ( EscapeSequence | ~('\\'|'"') )* '"' 
    ;

CHARSTRING
    : '\'' ( EscapeSequence | ~('\''|'\\') )* '\''
    ;

INT
    : Digit+
    ;

HEX
    : '0' [xX] HexDigit+
    ;

FLOAT
    : Digit+ '.' Digit* ExponentPart?
    | '.' Digit+ ExponentPart?
    | Digit+ ExponentPart
    ;

HEX_FLOAT
    : '0' [xX] HexDigit+ '.' HexDigit* HexExponentPart?
    | '0' [xX] '.' HexDigit+ HexExponentPart?
    | '0' [xX] HexDigit+ HexExponentPart
    ;

NAME : [a-zA-Z_][a-zA-Z_0-9]*
     ;

fragment
ExponentPart
    : [eE] [+-]? Digit+
    ;

fragment
HexExponentPart
    : [pP] [+-]? Digit+
    ;

fragment
EscapeSequence
    : '\\' [abfnrtvz"'\\]
    | '\\' '\r'? '\n'
    | DecimalEscape
    | HexEscape
    ;
    
fragment
DecimalEscape
    : '\\' Digit
    | '\\' Digit Digit
    | '\\' [0-2] Digit Digit
    ;
    
fragment
HexEscape
    : '\\' 'x' HexDigit HexDigit
    ;

fragment
Digit
    : [0-9]
    ;

fragment
HexDigit
    : [0-9a-fA-F]
    ;

COMMENT
    : '//' ~('\n')*? '\n' -> channel(HIDDEN)
    ;
    
WS  
    : [ \t\u000C\r\n]+ -> skip
    ;